package com.gitee.ywj1352.netty.server;

import com.gitee.ywj1352.common.Protocol.RpcRequest;
import com.gitee.ywj1352.common.Protocol.RpcResponse;
import com.gitee.ywj1352.netty.server.handler.RpcDecoder;
import com.gitee.ywj1352.netty.server.handler.RpcEncoder;
import com.gitee.ywj1352.netty.server.handler.RpcHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * @author wenjie.yang on 2019/10/23.
 */
@Service("rpcServer")
public class RpcServer {
    private static final Logger logger = LoggerFactory.getLogger(RpcServer.class);

    @Autowired
    private RpcHandler rpcHandler;

    private int port = 8090;

    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workGroup;

    public void start() throws InterruptedException {
        bossGroup = new NioEventLoopGroup();
        workGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                //初始化 channel 初始化器
                .childHandler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        // 此处的 LengthFieldBasedFrameDecoder 是 netty 用来解决 Tcp协议 常见的 粘包/半包 现象的  Decoder 对象
                        // 核心是 用length 来决定 一个包的大小
                        ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 0))
                                //等待 添加新的处理 解析 器 也就是 handler
                                .addLast(new RpcEncoder<RpcResponse>())
                                .addLast(new RpcDecoder<RpcRequest>())
                                .addLast(rpcHandler)
                        ;
                    }
                })
                //服务端处理客户端连接请求是顺序处理的，所以同一时间只能处理一个客户端连接，
                // 多个客户端来的时候，服务端将不能处理的客户端连接请求放在队列中等待处理，
                // backlog参数指定了队列的大小
                .option(ChannelOption.SO_BACKLOG, 128)
                //该参数用于设置TCP连接，当设置该选项以后，连接会测试链接的状态，这个选项用于可能长时间没有数据交流的
                //连接。当设置该选项以后，如果在两小时内没有数据的通信时，TCP会自动发送一个活动探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        //阻塞方法 表示 server 以及启动
        ChannelFuture future = bootstrap.bind("localhost", port).sync();
        logger.info("Server started on port {}", port);
    }

    public static void main(String[] args) throws InterruptedException {
        RpcServer rpcServer = new RpcServer();
        rpcServer.start();
    }

    @PostConstruct
    public void startServer() {
        try {
            start();
        } catch (InterruptedException e) {
            logger.error(e.getMessage());
        }
    }
}
